package com.sun.lg.factory;

import com.sun.lg.anno.*;
import com.sun.lg.service.TransferService;
import com.sun.lg.service.impl.TransferServiceImpl;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.FileFilter;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.URL;
import java.util.*;

public class AnnoBeanFactory {

    private static Map<Class<?>, Object> classMap = new HashMap<>();
    private static Map<String, Object> beanNameMap = new HashMap<>();
    private static final List<Class<? extends Annotation>> list = Arrays.asList(Component.class, Service.class, Repository.class);

    static {
        //获取存放所有的class的集合
        Set<Class<?>> classList = getPackageClassList("com.sun.lg");
        if (classList != null && classList.size() > 0) {
            //过滤包下所有class文件，并将符合要求的class文件放入map容器中
            setContainer(classList);
            //将容器对象中 被Autowired注解修饰的成员变量 获取其实例并注入到对应的对象成员变量上
            setContainerBeanAutowired();
            //为添加Transactional注解的类添加事务处理
            setContainerBeanTransactional();
            //将classMap遍历 得到每个Class对象中 修饰类的注解 将Component, Service, Repository 中的value提取缓存
            setBeanNameMap();
        }
    }

    //为添加Transactional注解的类添加事务处理
    private static void setContainerBeanTransactional() {
        if (!classMap.isEmpty() && classMap.size() > 0) {
            Set<Class<?>> classSet = classMap.keySet();
            if (classSet != null && classSet.size() > 0) {
                for (Class<?> aClass : classSet) {
                    if(aClass.isAnnotationPresent(Transactional.class)){
                        ProxyFactory proxyFactory = (ProxyFactory) classMap.get(ProxyFactory.class);
                        Object o = classMap.get(aClass);
                        Object object = proxyFactory.getCglibProxy(o);
                        classMap.put(aClass,object);
                    }
                }
            }
        }
    }

    //将classMap遍历 得到每个Class对象中 修饰类的注解 将Component, Service, Repository 中的value提取缓存
    private static void setBeanNameMap() {
        if (!classMap.isEmpty() && classMap.size() > 0) {
            Set<Map.Entry<Class<?>, Object>> entries = classMap.entrySet();
            for (Map.Entry<Class<?>, Object> entry : entries) {
                Class<?> aClass = entry.getKey();
                Object o = entry.getValue();
                String simpleName = aClass.getSimpleName();
                Component component = aClass.getAnnotation(Component.class);
                if (component != null) {
                    String value = component.value();
                    if (value != null && !"".equals(value)) {
                        simpleName = value;
                    }
                }
                Service service = aClass.getAnnotation(Service.class);
                if (service != null) {
                    String value = service.value();
                    if (value != null && !"".equals(value)) {
                        simpleName = value;
                    }
                }
                Repository repository = aClass.getAnnotation(Repository.class);
                if (repository != null) {
                    String value = repository.value();
                    if (value != null && !"".equals(value)) {
                        simpleName = value;
                    }
                }
                beanNameMap.put(simpleName, o);
            }
        }

    }

    public static Object getBean(Class a) {
        Object o = classMap.get(a);
        return o;
    }

    public static Object getBean(String name) {
        Object o = beanNameMap.get(name);
        return o;
    }

    //获取当前包下所有的class文件，并放入到集合中
    public static Set<Class<?>> getPackageClassList(String packageName) {
        //统一资源定位符
        String pName = packageName.replace(".", "/");
        URL resource = AnnoBeanFactory.class.getClassLoader().getResource(pName);
        if (resource == null) {
            return null;
        }

        Set<Class<?>> classSet = null;
        String protocol = resource.getProtocol();
        //判断协议是否是file
        if (protocol.equalsIgnoreCase("file")) {
            classSet = new HashSet<>();
            String path = resource.getPath();
            File file = new File(path);
            //获取File根目录及其所有子目录的文件
            getClassBean(classSet, file, packageName);
        }
        return classSet;
    }

    //获取File根目录及其所有子目录的文件
    private static void getClassBean(Set<Class<?>> classSet, File file, String packageName) {
        //第一次进来传的是包路径，所有如果不是文件夹那么就直接返回
        if (!file.isDirectory()) {
            return;
        }
        //过滤文件夹
        File[] files = file.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                if (pathname.isDirectory()) {
                    return true;
                } else {
                    //如果是文件，则进行处理后加入到集合中
                    String absolutePath = pathname.getAbsolutePath();
                    if (absolutePath.endsWith(".class")) {
                        //将文件路径中的 /或者\ 换成  .
                        absolutePath = absolutePath.replace(File.separator, ".");
                        //根据包名进行截取，获取文件的全限定类名
                        String substring = absolutePath.substring(absolutePath.indexOf(packageName));
                        String className = substring.substring(0, substring.lastIndexOf("."));
                        try {
                            //通过反射获取Class对象
                            if (!className.equalsIgnoreCase("com.sun.lg.servlet.TransferServlet")) {
                                Class<?> aClass = Class.forName(className);
                                classSet.add(aClass);
                            }
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
                return false;
            }
        });
        if (files != null) {
            for (File f : files) {
                //递归调用
                getClassBean(classSet, f, packageName);
            }
        }
    }

    //过滤包下所有class文件，并将符合要求的class文件放入map容器中
    public static void setContainer(Set<Class<?>> classList) {
        for (Class<?> aClass : classList) {
            //Component.class, Service.class, Repository.class 这个注解修饰的类过滤出来
            for (Class<? extends Annotation> al : list) {
                //判断类上的是否有Component.class, Service.class, Repository.class注解修饰
                if (aClass.isAnnotationPresent(al)) {
                    try {
                        //获取无参构造实例
                        Constructor declaredConstructor = aClass.getDeclaredConstructor();
                        declaredConstructor.setAccessible(true);
                        //通过无参构造创建对应的对象
                        Object o = declaredConstructor.newInstance();
                        classMap.put(aClass, o);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    //将容器对象中 被Autowired注解修饰的成员变量 获取其实例并注入到对应的对象成员变量上
    private static void setContainerBeanAutowired() {
        if (!classMap.isEmpty() && classMap.size() > 0) {
            Set<Class<?>> classSet = classMap.keySet();
            if (classSet != null && classSet.size() > 0) {
                for (Class<?> aClass : classSet) {
                    Field[] declaredFields = aClass.getDeclaredFields();
                    if (declaredFields != null) {
                        for (Field f : declaredFields) {
                            //获取被Autowired注解修饰的成员变量
                            if (f.isAnnotationPresent(Autowired.class)) {
                                //获取成员变量对应的class
                                Class<?> type = f.getType();
                                //从容器中取 如果是接口可能取不到
                                Object value = classMap.get(type);
                                if (value == null) {
                                    //获取成员变量对应的实例对象(被Autowired注解修饰的成员变量，是已经在容器中的class对象)
                                    Class<?> variableByClass = getVariableByClass(f, type);
                                    //从容器中获取对应的实例
                                    value = classMap.get(variableByClass);
                                }
                                //获取目标对象在容器中对应的实例对象
                                Object target = classMap.get(aClass);
                                f.setAccessible(true);
                                try {
                                    f.set(target, value);
                                } catch (IllegalAccessException e) {
                                    throw new RuntimeException("找不到");
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    //获取成员变量对应的实例对象(被Autowired注解修饰的成员变量，是已经在容器中的class对象)
    private static Class<?> getVariableByClass(Field f, Class<?> type) {
        Set<Class<?>> classSet = classMap.keySet();
        //如果是接口导致从容器中获取的为空 则再次获取接口对应的实现类，然后通过实现类从容器中获取
        Set<Class<?>> set = new HashSet<>();
        for (Class<?> aClass1 : classSet) {
            //判断获取成员变量对应的class是否是aClass1的接口或者父类
            if (type.isAssignableFrom(aClass1) && !type.equals(aClass1)) {
                set.add(aClass1);
            }
        }
        if (set != null && set.size() > 0) {
            Autowired autowired = f.getAnnotation(Autowired.class);
            String aBeanName = autowired.value();
            if (aBeanName == null || "".equals(aBeanName)) {
                if (set.size() == 1) {
                    Class<?> next = set.iterator().next();
                    return next;
                } else {
                    return null;
                }
            } else {
                for (Class<?> aClass1 : set) {
                    if (aClass1.getSimpleName().equals(aBeanName)) {
                        return aClass1;
                    }
                }
            }
        } else {
            return null;
        }
        return null;
    }

    public static void main(String[] args) throws Exception {
//        AnnoBeanFactory.getBean(TransferServiceImpl.class);
//        AnnoBeanFactory.getBean("transferService");

        TransferService service = (TransferService) AnnoBeanFactory.getBean("transferService");
        service.transfer("6029621011000", "6029621011001", 100);
    }

}
